package net.oschina.oldpig.sewer

import net.liftweb.json._
import javafx.scene.media.{Media, MediaPlayer}
import javafx.util.Duration
import javafx.scene.media.MediaPlayer.Status
import javafx.beans.value.{ObservableValue, ChangeListener}
import javafx.scene.image.Image
import javafx.application.Platform
import javax.print.DocFlavor.INPUT_STREAM
import javafx.stage.FileChooser

trait SewerController {self: SewerView =>
  var model: SewerModel = _

  def currentBag = model.bags.find(_.media == currentPlayer.getMedia).map(_.index)

  def currentPlayer = mediaView.getMediaPlayer


  /**
   * 按下Play/Pause按钮
   */
  def onPlayOrPause() {
    currentPlayer.getStatus match {
      case Status.PLAYING =>
        if (currentPlayer != null)
          currentPlayer.pause()
      case Status.PAUSED =>
        if (currentPlayer != null)
          currentPlayer.play()
      case _ => // Do nothing
    }
  }

  def onLoad():Unit = {
    val chooser = new FileChooser()
    chooser.getExtensionFilters.add(
      new FileChooser.ExtensionFilter("Sewer Package(*.sew)", "*.sew")
    )
    chooser.setTitle("Select Sewer Package")
    val selectedFile = chooser.showOpenDialog(SewerView.getScene.getWindow)
    if (selectedFile != null) {
      model = new SewerModel {
        override val pkgLoader = new JarClassLoader(selectedFile.toURI.toURL)
      }
      println(s"go!")
      playStream(0)
    }
  }


  /**
   * seek progressBar
   */
  def onSeek(pos: Double) {
    def unitAtPos(pos: Double): Int = {
      val borders = model.bags.map(_.stream.duration).scan(0L)(_ + _)
      (for ((_, i) <- borders.zipWithIndex.find {
        case (border, i) =>
          border >= pos * model.overallDuration / progressBar.getWidth
      }) yield i - 1).get
    }

    val bagAtPoint = unitAtPos(pos)
    model.overallPosition = model.overallDuration * pos / progressBar.getWidth
    val positionInCurrentStream = model.overallPosition - model.bags.slice(0, bagAtPoint).map(_.stream.duration).sum
    if (bagAtPoint != currentBag.get) {
      stopCurrentAnd(playStream(bagAtPoint, positionInCurrentStream))
    } else {
      currentPlayer.setStartTime(Duration.ZERO)
      currentPlayer.seek(Duration.millis(positionInCurrentStream))
    }

  }

  def stopCurrentAnd(action: => Unit): Unit = {
    val player = currentPlayer
    player.setOnStopped(new Runnable() {
      def run() {
//        println(s"#${currentBag.get} stopped")
        clearSlide()
        action
      }
    })
    player.stop()
  }

  /**
   *
   */
  def onVolume(newValue: Number) {
    if (volumeSlider.isValueChanging) {
      if (currentPlayer != null) {
        currentPlayer.setVolume(volumeSlider.getValue / volumeSlider.getMax)
      }
    }
  }

  def initPlayer(player: MediaPlayer) {
    val index = model.bags.find(_.media == player.getMedia).map(_.index).get
    println(s"init #$index")
    player.setOnPlaying(new Runnable {
      def run() {
        //          //        if (stopRequested) {
        //          //          player.pause()
        //          //          stopRequested = false
        //          //        } else {
        playButton.setGraphic(imageViewPause)
        println(s"#$index onPlaying")
        //          //        }
      }
    })
    player.setOnPaused(new Runnable {
      def run() {
        println(s"#$index onPaused")
        playButton.setGraphic(imageViewPlay)
      }
    })
    player.setOnReady(new Runnable {
      def run() {
        println(s"#$index onReady")
      }
    })
    player.setCycleCount(1)

    player.setOnError(new Runnable {
      def run() {
        println("Video error " + player.getError)
      }
    })
    player.setOnEndOfMedia(new Runnable() {
      def run() {
        if (currentBag.get == model.bags.size - 1) {
          // last
          playButton.setGraphic(imageViewPlay)
          //      stopRequested = true
          //      atEndOfMedia = true
        } else {
          stopCurrentAnd(playStream(currentBag.get + 1))
        }
      }
    })
    player.setStopTime(Duration.millis(model.bags(index).stream.duration))
    player.currentTimeProperty().addListener(onPlayerCurrentTimeChanged(player, index))

  }


  def clearSlide(): Unit = {
    slideView.setImage(null)
    model.currentSlide = -1
  }

  def onPlayerCurrentTimeChanged(player: MediaPlayer, index: Int) = new ChangeListener[Duration]() {
    def changed(observable: ObservableValue[_ <: Duration], oldValue: Duration, newValue: Duration) {
      val pos = newValue.toMillis
      model.overallPosition = model.bags.slice(0, index).map(_.stream.duration).sum + pos
      updateValues()
      for ((slide, i) <- model.slides.zipWithIndex.takeWhile {
        case (s, _) => s.cuePoint <= model.overallPosition
      }.lastOption) {
        if (i != model.currentSlide) {
          slideView.setImage(model.imageCache.get(i).getOrElse {
            val slideImg = new Image(model.pkgLoader.getResourceAsStream(slide.local))
            model.imageCache(i) = slideImg
            slideImg
          })
          model.currentSlide = i
        }
      }
    }
  }

  def updateValues() {
    if (playTime != null && progressBar != null && volumeSlider != null) {
      Platform.runLater(new Runnable {
        def run() {
          playTime.setText(model.formatTime(model.overallPosition.toLong, model.overallDuration.toLong))
          if (!progressBar.isDisabled && model.overallDuration > 0) {
            progressBar.setProgress(model.overallPosition / model.overallDuration)
          }
          if (!volumeSlider.isValueChanging) {
            if (currentPlayer != null)
              volumeSlider.setValue(math.round(currentPlayer.getVolume * volumeSlider.getMax))
          }
        }
      })
    }
  }


  def playStream(index: Int, at: Double = 0): Unit = {
    val player = model.retrievePlayer(index)
    println(s"player #$index current status = ${player.getStatus}")
    player.getStatus match {
      case Status.UNKNOWN =>
        Platform.runLater(new Runnable {
          def run() {
            player.setStartTime(Duration.millis(at))
            initPlayer(player)
            player.play()
          }
        })
      case Status.READY =>
        player.setStartTime(Duration.millis(at))
        player.play()
      case Status.STOPPED =>
        player.setOnPlaying(new Runnable {
          def run() {
            println(s"resuming #$index")
            player.setStartTime(Duration.ZERO)
            player.seek(Duration.millis(at))
          }
        })
        player.play()
    }
//    player.setStopTime(Duration.millis(model.bags(index).stream.duration))
    mediaView.setMediaPlayer(player)
//    println(s"player #$index current status = ${player.getStatus}")
//    player.getStatus match {
//      case Status.PLAYING =>
//        player.setStartTime(Duration.ZERO)
//        player.seek(Duration.millis(at))
//      case Status.READY =>
//        player.setStartTime(Duration.millis(at))
//      case Status.STOPPED =>
//        player.setOnPlaying(new Runnable {
//          def run() {
//            println(s"Seeking to $at")
//            player.setStartTime(Duration.ZERO)
//            player.seek(Duration.millis(at))
//                        player.setOnPlaying(null)
//          }
//        })
//      case _ => println("Unhandled status")
//    }
//    println(s"playing #$index")
//    player.play()
  }

}
